﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Diagnostics;
using ArtMath.Core.Data;
using System.Collections;
using ArtMath.CommonType;

namespace ArtMath.Core.Helper
{
    public static class Utility
    {
        #region APIs
        [DllImport("user32.dll")]
        public static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(IntPtr wnd, int msg, IntPtr wP, ref MessageContent lP);
        [DllImport("shell32.dll", ExactSpelling = true)]
        public static extern void ILFree(IntPtr pidlList);
        [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        public static extern IntPtr ILCreateFromPathW(string pszPath);
        [DllImport("shell32.dll", ExactSpelling = true)]
        public static extern int SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags);
        [DllImport("user32.dll", EntryPoint = "GetDoubleClickTime")]
        public static extern int GetDblClickInterval();
        #endregion
        static readonly double[] angleSplits = new double[] {
            -Math.PI / 24, Math.PI / 24, Math.PI / 8, 5 * Math.PI / 24, 7 * Math.PI / 24, 3 * Math.PI / 8,
            11 * Math.PI / 24, 13 * Math.PI / 24, 5 * Math.PI / 8, 17 * Math.PI / 24, 19 * Math.PI / 24,
            7 * Math.PI / 8, 23 * Math.PI / 24, 25 * Math.PI / 24, 9 * Math.PI / 8, 29 * Math.PI / 24,
            31 * Math.PI / 24, 11 * Math.PI / 8, 35 * Math.PI / 24, 37 * Math.PI / 24, 13 * Math.PI / 8,
            41 * Math.PI / 24, 43 * Math.PI / 24, 15 * Math.PI / 8, 47 * Math.PI / 24, 49 * Math.PI / 24
        };
        public static IList<Span> Intersect(IList<Span> Span1, IList<Span> Span2)
        {
            if (Span1 == null || Span1.Count == 0) return Span2;
            if (Span2 == null || Span2.Count == 0) return Span1;
            List<Span> result = new List<Span>();
            foreach (Span item1 in Span1)
            {
                foreach (Span item2 in Span2)
                {
                    if (item1 == Span.UniversalSet) return Span2;
                    if (item2 == Span.UniversalSet) return Span1;
                    Span? tmp = item1.Intersect(item2);
                    if (tmp.HasValue) result.Add(tmp.Value);
                }
            }
            return result;
        }
        public static string GetNextAvailableName(string name, Predicate<string> judge)
        {
            if (!judge(name)) return name;
            int count = 2;
            while (judge(name + " (" + count++ + ")")) ;
            return name + " (" + (count - 1) + ")";
        }
        public static bool Same(this IEnumerable objs)
        {
            HashSet<object> set = new HashSet<object>();
            bool succeeded = false;
            foreach (object item in objs)
            {
                if (set.Add(item))
                {
                    if (succeeded)
                    {
                        return false;
                    }
                    else
                    {
                        succeeded = true;
                    }
                }
            }
            return true;
        }
        public static double TwoPointsDistance(double x1, double y1, double x2, double y2)
        {
            double dx = x1 - x2, dy = y1 - y2;
            return Math.Sqrt(dx * dx + dy * dy);
        }
        //public static RectangleD GetSmallestSurroundRect(IEnumerable<Curve> pts)
        //{
        //    List<PointD> tmp = new List<PointD>();
        //    foreach (Curve item in pts)
        //    {
        //        tmp.AddRange(GetSmallestSurroundRect(item.Samples).GetVertices());
        //    }
        //    return GetSmallestSurroundRect(tmp);
        //}
        public static RectangleD GetSmallestSurroundRect<T>(IEnumerable<T> pts) where T : IEnumerable<PointD>
        {
            List<PointD> tmp = new List<PointD>();
            foreach (T item in pts)
            {
                tmp.AddRange(GetSmallestSurroundRect(item).GetVertices());
            }
            return GetSmallestSurroundRect(tmp);
        }
        /// <summary>
        /// 获取一组点的最小外接矩形
        /// </summary>
        /// <param name="points">点组</param>
        /// <returns></returns>
        public static RectangleD GetSmallestSurroundRect(IEnumerable<PointD> points)
        {
            double minX = double.PositiveInfinity, maxX = double.NegativeInfinity, 
                minY = double.PositiveInfinity, maxY = double.NegativeInfinity;
            foreach (PointD item in points)
            {
                if (item.X < minX)
                {
                    minX = item.X;
                }
                if (item.X > maxX)
                {
                    maxX = item.X;
                }
                if (item.Y < minY)
                {
                    minY = item.Y;
                }
                if (item.Y > maxY)
                {
                    maxY = item.Y;
                }
            }
            return new RectangleD(minX, minY, maxX - minX, maxY - minY);
        }
        /// <summary>
        /// 获取非共线三点所在圆的圆心坐标
        /// </summary>
        /// <param name="pt1">圆上一点1</param>
        /// <param name="pt2">圆上一点2</param>
        /// <param name="pt3">圆上一点3</param>
        /// <returns></returns>
        public static PointD GetCenter(PointD pt1, PointD pt2, PointD pt3)
        {
            PointD M = RatePoint(pt1, pt2), N = RatePoint(pt2, pt3);
            return GetCrossPoint(pt2.X - M.X, pt2.Y - M.Y,
                pt2.X * M.X + pt2.Y * M.Y - M.X * M.X - M.Y * M.Y, pt2.X - N.X, pt2.Y - N.Y,
                pt2.X * N.X + pt2.Y * N.Y - N.X * N.X - N.Y * N.Y);
        }
        /// <summary>
        /// 计算两向量夹角
        /// </summary>
        /// <param name="v1Start">向量1起点坐标</param>
        /// <param name="v1Stop">向量1终点坐标</param>
        /// <param name="v2Start">向量2起点坐标</param>
        /// <param name="v2Stop">向量2终点坐标</param>
        /// <returns>两向量夹角（单位：弧度，取值范围：[0, π]）</returns>
        public static double GetAngle(PointD v1Start, PointD v1Stop, PointD v2Start, PointD v2Stop)
        {
            return GetAngle(v1Stop - v1Start, v2Stop - v2Start);
        }
        public static double GetAngle(PointD v1, PointD v2)
        {
            //if (v1.IsEmpty || v2.IsEmpty) return 0;
            return Math.Acos((v1.X * v2.X + v1.Y * v2.Y) / (Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y) * Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y)));
        }
        public static double GetRotatedAngle(PointD v1, PointD v2)
        {
            return GetAngle(v1, v2) * Math.Sign(CrossProduct(v1, v2));
        }
        /// <summary>
        /// 计算两向量的数量积（点积、内积、点乘）
        /// </summary>
        /// <param name="v1Start">向量1起点坐标</param>
        /// <param name="v1Stop">向量1终点坐标</param>
        /// <param name="v2Start">向量2起点坐标</param>
        /// <param name="v2Stop">向量2终点坐标</param>
        /// <returns></returns>
        public static double ScalarProduct(PointD v1Start, PointD v1Stop, PointD v2Start, PointD v2Stop)
        {
            return ScalarProduct(v1Stop - v1Start, v2Stop - v2Start);
        }
        public static double ScalarProduct(PointD v1, PointD v2)
        {
            return v1.X * v2.X + v1.Y * v2.Y;
        }
        public static bool IsParallel(PointD v1Start, PointD v1Stop, PointD v2Start, PointD v2Stop)
        {
            return IsParallel(v1Stop - v1Start, v2Stop - v2Start);
        }
        public static bool IsParallel(PointD v1, PointD v2)
        {
            if (v1.IsEmpty || v2.IsEmpty)
            {
                return true;
            }
            return (v1.X * v2.Y).AlmostEqual(v1.Y * v2.X);
        }
        public static bool IsVertical(PointD v1Start, PointD v1Stop, PointD v2Start, PointD v2Stop)
        {
            return IsVertical(v1Stop - v1Start, v2Stop - v2Start);
        }
        public static bool IsVertical(PointD v1, PointD v2)
        {
            if (v1.IsEmpty || v2.IsEmpty)
            {
                return true;
            }
            return ScalarProduct(v1, v2).AlmostEqual(0);
        }
        /// <summary>
        /// 计算两向量的向量积（叉积、外积、叉乘）
        /// </summary>
        /// <param name="v1Start">向量1起点坐标</param>
        /// <param name="v1Stop">向量1终点坐标</param>
        /// <param name="v2Start">向量2起点坐标</param>
        /// <param name="v2Stop">向量2终点坐标</param>
        /// <returns></returns>
        public static double CrossProduct(PointD v1Start, PointD v1Stop, PointD v2Start, PointD v2Stop)
        {
            return CrossProduct(v1Stop - v1Start, v2Stop - v2Start);
        }
        public static double CrossProduct(PointD v1, PointD v2)
        {
            return v1.X * v2.Y - v1.Y * v2.X;
        }
        public static bool OnSameSide(PointD pointOnLine1, PointD pointOnLine2, PointD pt1, PointD pt2)
        {
            return Math.Sign(CrossProduct(pointOnLine1, pointOnLine2, pointOnLine1, pt1)) ==
                Math.Sign(CrossProduct(pointOnLine1, pointOnLine2, pointOnLine1, pt2));
        }
        public static bool OnSameSide(PointD pointOnLine, double k, PointD pt1, PointD pt2)
        {
            return Sign(pointOnLine, k, pt1) == Sign(pointOnLine, k, pt2);
        }
        /// <summary>
        /// 计算由3个点构成的角的角度
        /// </summary>
        /// <param name="vertex">角顶点坐标</param>
        /// <param name="pt1">角一边另一个点的坐标</param>
        /// <param name="pt2">角另一边另一个点的坐标</param>
        /// <returns>角的角度（单位：弧度，取值范围：[0, π]）</returns>
        public static double GetAngle(PointD vertex, PointD pt1, PointD pt2)
        {
            return GetAngle(vertex, pt1, vertex, pt2);
        }
        //public static double GetDirAngle(PointD watch, PointD start, PointD end)
        //{
        //    return watch.GetAngle(end) - watch.GetAngle(start);
        //}
        ///// <summary>
        ///// 根据到角公式计算直线1按逆时针方向旋转到与直线2重合时所转过的角度
        ///// </summary>
        ///// <param name="k1">直线1斜率</param>
        ///// <param name="k2">直线2斜率</param>
        ///// <returns>转过的角度（单位：弧度）</returns>
        //public static double getAngle(double k1, double k2)
        //{
        //    if (isNumber(k1) && isNumber(k2))
        //    {
        //        return Math.Atan((k2 - k1) / (1 + k2 * k1));
        //    }
        //    else if (double.IsInfinity(k1))
        //    {
        //        return 0;
        //    }
        //    return double.NaN;
        //}
        public static PointD CalcLength(PointD location, SizeD size, double angle)
        {
            angle = Angle.StandardizeDegree(angle);
            double std = (angle % 90) * Angle.DToR, cos = Math.Cos(std), sin = Math.Sin(std);
            if (angle == 0)
            {
                return new PointD(location.X, size.Width - location.X);
            }
            else if (angle > 0 && angle < 90)
            {
                return new PointD(Math.Min(location.X / cos, (size.Height - location.Y) / sin),
                    Math.Min((size.Width - location.X) / cos, location.Y / sin));
            }
            else if (angle == 90)
            {
                return new PointD(size.Height - location.Y, location.Y);
            }
            else if (angle > 90 && angle < 180)
            {
                return new PointD(Math.Min((size.Height - location.Y) / cos, (size.Width - location.X) / sin),
                    Math.Min(location.Y / cos, location.X / sin));
            }
            else if (angle == 180)
            {
                return new PointD(size.Width - location.X, location.X);
            }
            else if (angle > 180 && angle < 270)
            {
                return new PointD(Math.Min((size.Width - location.X) / cos, location.Y / sin),
                    Math.Min(location.X / cos, (size.Height - location.Y) / sin));
            }
            else if (angle == 270)
            {
                return new PointD(location.Y, size.Height - location.Y);
            }
            else
            {
                return new PointD(Math.Min(location.Y / cos, location.X / sin),
                    Math.Min((size.Width - location.X) / sin, (size.Height - location.Y) / cos));
            }
        }
        /// <summary>
        /// 获取和指定角度最接近的修正角度（15°的倍数角）
        /// </summary>
        /// <param name="angle">指定角度（弧度）</param>
        /// <returns></returns>
        public static double GetFixedAngle(double angle)
        {
            if (!angle.IsNumber()) return 0;
            int l = 0, h = angleSplits.Length - 1, m;
            while (l <= h)
            {
                m = (l + h) / 2;
                if (m < angleSplits.Length - 1)
                {
                    if (angleSplits[m] <= angle && angleSplits[m + 1] > angle)
                    {
                        return (angleSplits[m] + angleSplits[m + 1]) / 2;
                    }
                    else
                    {
                        if (angleSplits[m] > angle)
                        {
                            h = m - 1;
                        }
                        else
                        {
                            l = m + 1;
                        }
                    }
                }
            }
            return 0;
        }
        /// <summary>
        /// 判断从旧点到新点是否是顺时针
        /// </summary>
        /// <param name="center">观察点坐标</param>
        /// <param name="oldpt">旧点坐标</param>
        /// <param name="newpt">新点坐标</param>
        /// <returns>顺时针返回True，逆时针返回False</returns>
        public static bool IsClockwise(PointD center, PointD oldpt, PointD newpt)
        {
            return CrossProduct(center, oldpt, center, newpt) <= 0;
        }
        #region ConvexHull
        class ConvexHullComparer : IComparer<PointD>
        {
            public PointD LB;
            public int Compare(PointD x, PointD y)
            {
                double cp = CrossProduct(LB, y, LB, x);
                return Math.Sign(cp == 0 ? LB.DistanceTo(y) - LB.DistanceTo(x) : cp);
            }
        }
        public static PointD[] ConvexHull(params PointD[] samples)
        {
            List<PointD> result = new List<PointD>();
            int leftbottom = 0;
            for (int i = 1; i < samples.Length; i++)
            {
                if (samples[i].Y < samples[leftbottom].Y || (samples[i].Y == samples[leftbottom].Y && samples[i].X < samples[leftbottom].X))
                {
                    leftbottom = i;
                }
            }
            result.Add(samples[leftbottom]);
            Swap(ref samples[0], ref samples[leftbottom]);
            Array.Sort(samples, 1, samples.Length - 1, new ConvexHullComparer() { LB = samples[0] });
            result.Add(samples[1]);
            result.Add(samples[2]);
            for (int i = 3; i < samples.Length; i++)
            {
                while (result.Count > 1 && CrossProduct(result[result.Count - 2], samples[i], result[result.Count - 2], result[result.Count - 1]) >= 0)
                {
                    result.RemoveAt(result.Count - 1);
                }
                result.Add(samples[i]);
            }
            return result.ToArray();
        }
        public static void Swap<T>(ref T a, ref T b)
        {
            T tmp = a;
            a = b;
            b = tmp;
        }
        #endregion
        /// <summary>
        /// 鼠标控制旋转一组点
        /// </summary>
        /// <param name="senderOldLocation">旋转操作点的初始位置</param>
        /// <param name="senderNewLocation">旋转操作点的当前位置</param>
        /// <param name="sender_rotateCenter">旋转操作点的旋转中心</param>
        /// <param name="lambda">旋转中心和旋转操作点的距离的lambda倍（0表示长度不变）</param>
        /// <param name="receiverPoints">跟随旋转操作点旋转的点</param>
        /// <param name="receiver_rotateCenters">旋转点的旋转中心（若为null则为旋转中心）</param>
        /// <returns></returns>
        public static PointD[] Rotate(PointD senderOldLocation, PointD senderNewLocation, PointD sender_rotateCenter,
            IList<KeyValuePair<PointD, double>> receiverPointsInfo, IList<PointD> receiver_rotateCenters = null)
        {
            PointD[] result = new PointD[receiverPointsInfo.Count];
            double angle = Math.Atan2(senderOldLocation.Y - sender_rotateCenter.Y, sender_rotateCenter.X - senderOldLocation.X) - Math.Atan2(senderNewLocation.Y - sender_rotateCenter.Y, sender_rotateCenter.X - senderNewLocation.X);
            for (int i = 0; i < result.Length; i++)
            {
                PointD center = receiver_rotateCenters == null ? sender_rotateCenter : receiver_rotateCenters[i < receiver_rotateCenters.Count ? i : receiver_rotateCenters.Count - 1];
                double tmp = receiverPointsInfo[i].Value;
                PointD pt = receiverPointsInfo[i].Key;
                if (tmp > 0)
                {
                    tmp *= sender_rotateCenter.DistanceTo(senderNewLocation) / center.DistanceTo(pt);
                    pt = tmp * (pt - center) + center;
                }
                result[i] = pt.RotateAt(center, angle);
            }
            return result;
        }
        public static PointD Rotate(PointD senderOld, PointD senderNew, PointD senderCenter,
    PointD receiverOld, PointD receiverCenter, double ratio)
        {
            return (ratio * senderCenter.DistanceTo(senderNew) / receiverCenter.DistanceTo(receiverOld) *
                (receiverOld - receiverCenter) + receiverCenter).RotateAt(receiverCenter,
                Math.Atan2(senderOld.Y - senderCenter.Y, senderCenter.X - senderOld.X) -
                Math.Atan2(senderNew.Y - senderCenter.Y, senderCenter.X - senderNew.X));
        }
        public static PointD Rotate(PointD senderOld, PointD senderNew, PointD rotateCenter, PointD receiverOld, double ratio)
        {
            Trace.WriteLine("FFF");
            return Rotate(senderOld, senderNew, rotateCenter, receiverOld, rotateCenter, ratio);
        }

        /// <summary>
        /// 求两直线的交点
        /// </summary>
        /// <param name="A1">直线1的x的系数</param>
        /// <param name="B1">直线1的y的系数</param>
        /// <param name="C1">直线1的常数项系数</param>
        /// <param name="A2">直线2的x的系数</param>
        /// <param name="B2">直线2的y的系数</param>
        /// <param name="C2">直线2的常数项系数</param>
        /// <returns>交点坐标</returns>
        public static PointD GetCrossPoint(double A1, double B1, double C1, double A2, double B2, double C2)
        {
            double D = A1 * B2 - B1 * A2;
            return new PointD(C1 * B2 - B1 * C2, A1 * C2 - C1 * A2) / D;
        }
        /// <summary>
        /// 获取两直线交点
        /// </summary>
        /// <param name="p1">直线1上的一点</param>
        /// <param name="k1">直线1的斜率</param>
        /// <param name="p2">直线2上的一点</param>
        /// <param name="k2">直线2的斜率</param>
        /// <returns></returns>
        public static PointD GetCrossPoint(PointD p1, double k1, PointD p2, double k2)
        {
            //此处的ABC非直线方程系数，而是克莱姆法则中的
            //A1x+B1y=C1
            //A2x+B2y=C2
            double A1, B1, C1, A2, B2, C2;
            if (double.IsInfinity(k1))
            {
                A1 = 1;
                B1 = 0;
                C1 = p1.X;
            }
            else
            {
                A1 = k1;
                B1 = -1;
                C1 = k1 * p1.X - p1.Y;
            }
            if (double.IsInfinity(k2))
            {
                A2 = 1;
                B2 = 0;
                C2 = p2.X;
            }
            else
            {
                A2 = k2;
                B2 = -1;
                C2 = k2 * p2.X - p2.Y;
            }
            return GetCrossPoint(A1, B1, C1, A2, B2, C2);
        }
        public static int Sign(PointD pt1, PointD pt2, PointD mousePoint)
        {
            return Sign(pt1, pt1.GetSlope(pt2), mousePoint);
        }
        /// <summary>
        /// 判断某点在直线哪侧
        /// </summary>
        /// <param name="pointOnLine">直线上一点</param>
        /// <param name="slope">直线斜率</param>
        /// <param name="pt">某点</param>
        /// <returns>在直线上方（斜率存在）或直线（斜率不存在）右侧，返回 1 ;在直线下方或直线左侧，返回 -1 ;在直线上，返回 0</returns>
        public static int Sign(PointD pointOnLine, double slope, PointD pt)
        {
            if (double.IsInfinity(slope))
            {
                return Math.Sign(pt.X - pointOnLine.X);
            }
            else
            {
                return Math.Sign((pt.X - pointOnLine.X) * slope + pointOnLine.Y - pt.Y);
            }
        }
        public static int Sign(double A, double B, double C, PointD pt)
        {
            if (B == 0)
            {
                return Math.Sign(pt.X + C / A);
            }
            else
            {
                return Math.Sign(-A / B * pt.X - C / B - pt.Y);
            }
        }
        /// <summary>
        /// 判断三点是否共线
        /// </summary>
        /// <param name="pt1">点1坐标</param>
        /// <param name="pt2">点2坐标</param>
        /// <param name="pt3">点3坐标</param>
        /// <returns></returns>
        public static bool OnOneLine(PointD pt1, PointD pt2, PointD pt3)
        {
            return ((pt2.Y - pt1.Y) * (pt3.X - pt2.X)).AlmostEqual((pt2.X - pt1.X) * (pt3.Y - pt2.Y));
        }
        /// <summary>
        /// 计算定比分点坐标
        /// </summary>
        /// <param name="pt1">点1坐标</param>
        /// <param name="pt2">点2坐标</param>
        /// <param name="rate">比例（不可为-1）</param>
        /// <returns>定比分点坐标</returns>
        public static PointD RatePoint(PointD pt1, PointD pt2, double rate = 1)
        {
            return (pt1 + pt2 * rate) / (1 + rate);
        }
        /// <summary>
        /// 计算斜率为k1的直线逆时针旋转到与斜率为k2的直线重合时所扫过的角度（单位：弧度）
        /// </summary>
        /// <param name="k1">起点直线斜率</param>
        /// <param name="k2">终点直线斜率</param>
        /// <returns></returns>
        public static double GetArrivalAngle(double k1, double k2)
        {
            if (double.IsInfinity(k1))
            {
                return Math.PI / 2 + (k2 >= 0 ? 1 : -1) * Math.Abs(Math.Atan(k2));
            }
            if (double.IsInfinity(k2))
            {
                return Math.PI / 2 + (k1 >= 0 ? -1 : 1) * Math.Abs(Math.Atan(k1));
            }
            if (k1 * k2 == -1) return Math.PI / 2;
            return Math.Atan((k2 - k1) / (1 + k1 * k2));
        }
        /// <summary>
        /// 计算斜率为k1的直线和斜率为k2的直线的夹角（[0, π/2]）
        /// </summary>
        /// <param name="k1">直线1的斜率</param>
        /// <param name="k2">直线2的斜率</param>
        /// <returns></returns>
        public static double GetIncludedAngle(double k1, double k2)
        {
            if (double.IsInfinity(k1))
            {
                return Math.PI / 2 + (k2 >= 0 ? -1 : 1) * Math.Atan(k2);
            }
            if (double.IsInfinity(k2))
            {
                return Math.PI / 2 + (k1 >= 0 ? -1 : 1) * Math.Atan(k1);
            }
            if (k1 * k2 == -1) return Math.PI / 2;
            return Math.Atan(Math.Abs((k2 - k1) / (1 + k1 * k2)));
        }
        /// <summary>
        /// 求三角形内心坐标
        /// </summary>
        /// <param name="pt1"></param>
        /// <param name="pt2"></param>
        /// <param name="pt3"></param>
        /// <returns></returns>
        public static PointD GetInCenter(PointD pt1, PointD pt2, PointD pt3)
        {
            double len1 = pt2.DistanceTo(pt3), len2 = pt1.DistanceTo(pt3), len3 = pt1.DistanceTo(pt2);
            return new PointD((pt1.X * len1 + pt2.X * len2 + pt3.X * len3) / (len1 + len2 + len3),
                (pt1.Y * len1 + pt2.Y * len2 + pt3.Y * len3) / (len1 + len2 + len3));
        }
        public static bool GetInscribedCircleCenter(IList<PointD> pts, out PointD? result)
        {
            if (pts.Count < 3)
            {
                result = null;
                return false;
            }
            result = GetCrossPoint(pts[1], GetAngularBisectorSlope(pts[1], pts[0], pts[2]), pts[2], GetAngularBisectorSlope(pts[2], pts[1], pts.Count == 3 ? pts[0] : pts[3]));
            double dist = result.Value.DistanceTo(pts[0], pts[1]);
            for (int i = 3; i < pts.Count; i++)
            {
                int iNext = i == pts.Count - 1 ? 0 : i + 1;
                if (!dist.AlmostEqual(result.Value.DistanceTo(pts[i], pts[iNext])))
                {
                    return false;
                }
            }
            return true;
        }
        public static double GetAngularBisectorSlope(PointD vertex, PointD pt1, PointD pt2)
        {
            return Math.Tan((vertex.GetAngle(pt1) + vertex.GetAngle(pt2)) / 2);
        }
        /// <summary>
        /// 求三角形外心坐标
        /// </summary>
        /// <param name="pt1"></param>
        /// <param name="pt2"></param>
        /// <param name="pt3"></param>
        /// <returns></returns>
        public static PointD GetCircumCenter(PointD pt1, PointD pt2, PointD pt3)
        {
            double A1 = 2 * (pt2.X - pt1.X), B1 = 2 * (pt2.Y - pt1.Y),
                C1 = pt2.X * pt2.X + pt2.Y * pt2.Y - pt1.X * pt1.X - pt1.Y * pt1.Y,
                A2 = 2 * (pt3.X - pt2.X), B2 = 2 * (pt3.Y - pt2.Y),
                C2 = pt3.X * pt3.X + pt3.Y * pt3.Y - pt2.X * pt2.X - pt2.Y * pt2.Y;
            return new PointD((B2 * C1 - B1 * C2) / (B2 * A1 - B1 * A2), (A1 * C2 - A2 * C1) / (B2 * A1 - B1 * A2));
        }
        /// <summary>
        /// 求三角形垂心坐标
        /// </summary>
        /// <param name="pt1"></param>
        /// <param name="pt2"></param>
        /// <param name="pt3"></param>
        /// <returns></returns>
        public static PointD GetOrthoCenter(PointD pt1, PointD pt2, PointD pt3)
        {
            return new PointD(((pt1.Y - pt2.Y) * (pt2.Y - pt3.Y) * (pt3.Y - pt1.Y) - pt1.X * pt2.X * (pt1.Y - pt2.Y) - pt2.X * pt3.X * (pt2.Y - pt3.Y) - pt3.X * pt1.X * (pt3.Y - pt1.Y)) / (pt3.X * (pt1.Y - pt2.Y) + pt1.X * (pt2.Y - pt3.Y) + pt2.X * (pt3.Y - pt1.Y)),
                (pt1.Y * pt2.Y * (pt1.X - pt2.X) + pt2.Y * pt3.Y * (pt2.X - pt3.X) + pt3.Y * pt1.Y * (pt3.X - pt1.X) - (pt1.X - pt2.X) * (pt2.X - pt3.X) * (pt3.X - pt1.X)) / (pt3.X * (pt1.Y - pt2.Y) + pt1.X * (pt2.Y - pt3.Y) + pt2.X * (pt3.Y - pt1.Y)));
        }
        public static PointD GetBaryCenter(IList<PointD> pts)
        {
            switch (pts.Count)
            {
                case 1:
                    return pts[0];
                case 2:
                    return (pts[0] + pts[1]) / 2;
                default:
                    double sall = 0;
                    double[] S = new double[pts.Count - 2];
                    PointD res = new PointD(0, 0);
                    for (int i = 1; i < pts.Count - 1; i++)
                    {
                        S[i - 1] = (pts[i].X - pts[0].X) * (pts[i + 1].Y - pts[0].Y) - (pts[i].Y - pts[0].Y) * (pts[i + 1].X - pts[0].X);
                        sall += S[i - 1];
                        res.X += S[i - 1] * (pts[i].X + pts[i + 1].X + pts[0].X) / 3;
                        res.Y += S[i - 1] * (pts[i].Y + pts[i + 1].Y + pts[0].Y) / 3;
                    }
                    res.X /= sall;
                    res.Y /= sall;
                    return res;
            }
        }
        /// <summary>
        /// 根据像素间隔计算标尺间隔
        /// </summary>
        /// <param name="f">像素间隔</param>
        /// <returns></returns>
        public static double ConvertAxisInterval(double f)
        {
            f /= 100;
            double slj = Math.Pow(10, Math.Ceiling(Math.Log10(f))), split = slj * 0.2;
            return 1 / (f <= split ? split : slj);
        }
        public static double GetSweepRate(double start, double sweep, double angle)
        {
            if (sweep >= 0)
            {
                if (angle >= start)
                {
                    return (angle - start) / Math.Abs(sweep);
                }
                else
                {
                    return (Angle.RadRound - start + angle) / Math.Abs(sweep);
                }
            }
            else
            {
                if (angle >= start)
                {
                    return (start + Angle.RadRound - angle) / Math.Abs(sweep);
                }
                else
                {
                    return (start - angle) / Math.Abs(sweep);
                }
            }
        }
        public static double GetSweepAngle(double start, double end, bool clockwise)
        {
            start = Angle.StandardizeRad(start);
            end = Angle.StandardizeRad(end);
            if (start >= end)
            {
                return clockwise ? start - end : Angle.RadRound - start + end;
            }
            else
            {
                return clockwise ? Angle.RadRound - end + start : end - start;
            }
        }
        public static bool InSweepRange(double start, double sweep, double angle)
        {
            if (Math.Abs(sweep) >= Angle.RadRound) return true;
            start = Angle.StandardizeRad(start);
            angle = Angle.StandardizeRad(angle);
            double end = Angle.StandardizeRad(start + sweep);
            if (sweep >= 0)
            {
                if (end >= start)
                {
                    return angle >= start && angle <= end;
                }
                else
                {
                    return angle >= start || angle <= end;
                }
            }
            else
            {
                if (end >= start)
                {
                    return angle <= start || angle >= end;
                }
                else
                {
                    return angle >= end && angle <= start;
                }
            }
        }
        /// <summary>
        /// 判断+-是否是正负号
        /// </summary>
        /// <param name="context"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static bool IsSign(string context, int index)
        {
            if (context != null && index >= 0 && index < context.Length)
            {
                switch (context[index])
                {
                    case '+':
                    case '-':
                        if (index == 0) return true;
                        switch (context[index - 1])
                        {
                            case '+':
                            case '-':
                            case '*':
                            case '/':
                            case '(':
                            case 'e':
                            case 'E':
                            case '^':
                                return true;
                        }
                        break;
                }
            }
            return false;
        }
        public static bool IsSign(StringBuilder context, int index)
        {
            if (context != null && index >= 0 && index < context.Length)
            {
                switch (context[index])
                {
                    case '+':
                    case '-':
                        if (index == 0) return true;
                        switch (context[index - 1])
                        {
                            case '+':
                            case '-':
                            case '*':
                            case '/':
                            case '(':
                            case 'e':
                            case 'E':
                            case '^':
                                return true;
                        }
                        break;
                }
            }
            return false;
        }
        public static string DecoratePolynomial(string exp)
        {
            StringBuilder sb = new StringBuilder(exp);
            for (int i = 0; i < sb.Length; i++)
            {
                switch (sb[i])
                {
                    case '+':
                    case '-':
                    case '/':
                    case '^':
                    case '=':
                        if (!IsSign(sb, i))
                        {
                            sb.Insert(i + 1, ' ');
                            sb.Insert(i, ' ');
                            i += 2;
                        }
                        break;
                    case '*':
                        if ((char.IsLetter(sb[i + 1]) || sb[i + 1] == 'π' || sb[i + 1] == '(') && 
                            (char.IsDigit(sb[i - 1]) || sb[i - 1] == ')'))
                        {
                            sb.Remove(i, 1);
                            i--;
                        }
                        else
                        {
                            sb.Insert(i + 1, ' ');
                            sb.Insert(i, ' ');
                            i += 2;
                        }
                        break;
                }
            }
            return sb.ToString();
        }
        enum BeforeLeftBracket
        {
            Empty,
            Sign,
            FunctionName,
        }
        /// <summary>
        /// 标准化多项式
        /// </summary>
        /// <param name="exp">表达式</param>
        /// <returns></returns>
        public static string StandardizePolynomial(string exp)
        {
            StringBuilder sb = new StringBuilder(exp);
            for (int i = 0; i < sb.Length; i++)
            {
                //规范符号
                switch (sb[i])
                {
                    case '（':
                    case '[':
                    case '{':
                    case '【':
                        sb[i] = '(';
                        break;
                    case '）':
                    case ']':
                    case '}':
                    case '】':
                        sb[i] = ')';
                        break;
                    case '，':
                        sb[i] = ',';
                        break;
                    case '。':
                        sb[i] = '.';
                        break;
                    case '＋':
                        sb[i] = '+';
                        break;
                    case '－':
                    case '—':
                        sb[i] = '-';
                        break;
                    case '×':
                        sb[i] = '*';
                        break;
                    case '÷':
                        sb[i] = '/';
                        break;
                }
                if (IsSign(sb, i))
                {
                    switch (sb[i])
                    {
                        case '+':
                            sb.Remove(i, 1);
                            i--;
                            continue;
                        case '-':
                            if (i > 0)
                            {
                                switch (sb[i - 1])
                                {
                                    case '+':
                                        sb.Remove(i - 1, 1);
                                        i--;
                                        continue;
                                    case '-':
                                        sb.Remove(i, 1);
                                        i--;
                                        if (i == 0)
                                        {
                                            sb.Remove(0, 1);
                                            i--;
                                        }
                                        else
                                        {
                                            sb[i] = '+';
                                        }
                                        continue;
                                }
                            }
                            break;
                    }
                }
                //在省略乘号的地方补上乘号
                if (char.IsLetter(sb[i]) && sb[i] != 'd' && sb[i] != 'D' && sb[i] != 'r' && sb[i] != 'R' ||
                    sb[i] == 'π' || sb[i] == '(')
                {
                    //科学计数法的e前不插入*
                    if (sb[i] == 'e' || sb[i] == 'E')
                    {
                        int j = i + 1, sign = 0;
                        while (j < sb.Length && (char.IsDigit(sb[j]) || (sb[j] == '-' || sb[j] == '+') && j == i + 1))
                        {
                            if (sb[j] == '-' || sb[j] == '+') sign++;
                            j++;
                        }
                        if (j - i >= 2 + sign) continue;
                    }
                    if (i > 0 && (char.IsDigit(sb[i - 1]) || sb[i - 1] == ')'))
                    {
                        sb.Insert(i, '*');
                        i++;
                        continue;
                    }
                }
            }
            //删除冗余括号，并增补必要括号
            Stack<KeyValuePair<int, BeforeLeftBracket>> st = new Stack<KeyValuePair<int, BeforeLeftBracket>>();
            for (int i = 0; i < sb.Length; i++)
            {
                switch (sb[i])
                {
                    case '(':
                        BeforeLeftBracket type = BeforeLeftBracket.Empty;
                        if (i > 0)
                        {
                            if (IsSign(sb, i - 1))
                            {
                                type = BeforeLeftBracket.Sign;
                                sb.Insert(i - 1, "(0");
                                st.Push(new KeyValuePair<int, BeforeLeftBracket>(i - 1, BeforeLeftBracket.Empty));
                                i += 2;
                            }
                            else if (char.IsLetter(sb[i - 1]))
                            {
                                type = BeforeLeftBracket.FunctionName;
                            }
                        }
                        st.Push(new KeyValuePair<int, BeforeLeftBracket>(i, type));
                        break;
                    case ')':
                        KeyValuePair<int, BeforeLeftBracket> lbrk = st.Pop();
                        //try
                        //{
                        //    double val = Calculation.CalculateWithNoFunc(sb.ToString(lbrk + 1, i - lbrk - 1));
                        //    string sval = val.ToString();
                        //    if (lbrk > 0 && char.IsLetter(sb[lbrk - 1]))
                        //    {
                        //        sb.Remove(lbrk + 1, i - lbrk - 1);
                        //        sb.Insert(lbrk + 1, sval);
                        //        i += sval.Length - i + lbrk + 1;
                        //        continue;
                        //    }
                        //    else
                        //    {
                        //        sb.Remove(lbrk, i - lbrk + 1);
                        //        sb.Insert(lbrk, "(" + sval + ")");
                        //        i += sval.Length - i + lbrk + 1;
                        //    }
                        //}
                        //catch (Exception)
                        //{
                        //}
                        switch (lbrk.Value)
                        {
                            case BeforeLeftBracket.Sign:
                                sb.Insert(i + 1, ')');
                                break;
                            case BeforeLeftBracket.FunctionName://函数的括号不去
                                continue;
                        }
                        int opri = LowestOperatorPriority(sb.ToString(lbrk.Key + 1, i - lbrk.Key - 1));
                        switch (opri)
                        {
                            case -1:
                                if (sb[lbrk.Key + 1] == '-')
                                {
                                    if (lbrk.Key > 0 && (sb[lbrk.Key - 1] == '*' || sb[lbrk.Key - 1] == '/' || sb[lbrk.Key - 1] == '^'))
                                    {
                                        break;
                                    }
                                    if (i < sb.Length - 1 && sb[i + 1] == '^')
                                    {
                                        break;
                                    }
                                }
                                sb.Remove(i, 1);
                                sb.Remove(lbrk.Key, 1);
                                i -= 2;
                                switch (sb[lbrk.Key])
                                {
                                    case '+'://去除正号
                                        sb.Remove(lbrk.Key, 1);
                                        i--;
                                        break;
                                    case '-':
                                        if (lbrk.Key > 0)
                                        {
                                            switch (sb[lbrk.Key - 1])
                                            {
                                                case '+'://去除负号前的+运算符
                                                    sb.Remove(lbrk.Key - 1, 1);
                                                    i--;
                                                    break;
                                                case '-':
                                                    sb.Remove(lbrk.Key, 1);
                                                    i--;
                                                    if (lbrk.Key == 1)//-(-x) => x
                                                    {
                                                        sb.Remove(lbrk.Key - 1, 1);
                                                        i--;
                                                    }
                                                    else//...-(-x) => ...+x
                                                    {
                                                        sb[lbrk.Key - 1] = '+';
                                                    }
                                                    break;
                                            }
                                        }
                                        break;
                                }
                                continue;
                            default:
                                if (lbrk.Key == 0)
                                {
                                    if (i == sb.Length - 1)
                                    {
                                        sb.Remove(i, 1);
                                        sb.Remove(lbrk.Key, 1);
                                        i -= 2;
                                        continue;
                                    }
                                    else
                                    {
                                        switch (sb[i + 1])
                                        {
                                            case '+':
                                            case '-':
                                                sb.Remove(i, 1);
                                                sb.Remove(lbrk.Key, 1);
                                                i -= 2;
                                                continue;
                                            case '*':
                                            case '/':
                                                switch (opri)
                                                {
                                                    case 2:
                                                    case 3:
                                                        sb.Remove(i, 1);
                                                        sb.Remove(lbrk.Key, 1);
                                                        i -= 2;
                                                        continue;
                                                }
                                                break;
                                        }
                                    }
                                }
                                else
                                {
                                    switch (sb[lbrk.Key - 1])
                                    {
                                        case '+':
                                        case '(':
                                            if (i == sb.Length - 1)
                                            {
                                                sb.Remove(i, 1);
                                                sb.Remove(lbrk.Key, 1);
                                                i -= 2;
                                                continue;
                                            }
                                            else
                                            {
                                                switch (sb[i + 1])
                                                {
                                                    case '+':
                                                    case '-':
                                                    case ')':
                                                        sb.Remove(i, 1);
                                                        sb.Remove(lbrk.Key, 1);
                                                        i -= 2;
                                                        continue;
                                                    case '*':
                                                    case '/':
                                                        switch (opri)
                                                        {
                                                            case 2:
                                                            case 3:
                                                                sb.Remove(i, 1);
                                                                sb.Remove(lbrk.Key, 1);
                                                                i -= 2;
                                                                continue;
                                                        }
                                                        break;
                                                }
                                            }
                                            break;
                                        case '-':
                                        case '*':
                                            switch (opri)
                                            {
                                                case 2:
                                                case 3:
                                                    sb.Remove(i, 1);
                                                    sb.Remove(lbrk.Key, 1);
                                                    i -= 2;
                                                    continue;
                                            }
                                            break;
                                        case '/':
                                            switch (opri)
                                            {
                                                case 2:
                                                    sb.Remove(i, 1);
                                                    sb.Remove(lbrk.Key, 1);
                                                    i -= 2;
                                                    continue;
                                            }
                                            break;
                                    }
                                }
                                break;
                        }
                        break;
                }
            }
            //删除多余的+-0、0+-、*/1、1*/、x^1
            int index;
            double num;
            for (int i = 0; i < sb.Length; i++)
            {
                switch (sb[i])
                {
                    case '+':
                        index = findNumberStartIndex(sb, i - 1, out num);
                        if (index < i && (index == 0 || sb[index - 1] == '+' || sb[index - 1] == '(') && num == 0)
                        {
                            sb.Remove(index, i - index + 1);
                            i -= i - index + 1;
                        }
                        else
                        {
                            index = findNumberEndIndex(sb, i + 1, out num);
                            if (index > i && (index == sb.Length - 1 || sb[index + 1] == '+' || sb[index + 1] == '-' || sb[index + 1] == ')') && num == 0)
                            {
                                sb.Remove(i, index - i + 1);
                                i--;
                            }
                        }
                        break;
                    case '-':
                        index = findNumberEndIndex(sb, i + 1, out num);
                        if (index > i && (index == sb.Length - 1 || sb[index + 1] == '+' || sb[index + 1] == '-' || sb[index + 1] == ')') && num == 0)
                        {
                            sb.Remove(i, index - i + 1);
                            i--;
                        }
                        break;
                    case '*':
                        index = findNumberStartIndex(sb, i - 1, out num);
                        if (index < i && (index == 0 || sb[index - 1] == '+' || sb[index - 1] == '-' || sb[index - 1] == '*' || sb[index - 1] == '(') && num == 1)
                        {
                            sb.Remove(index, i - index + 1);
                            i -= i - index + 1;
                        }
                        else
                        {
                            index = findNumberEndIndex(sb, i + 1, out num);
                            if (index > i && (index == sb.Length - 1 || sb[index + 1] == '+' || sb[index + 1] == '-' || sb[index + 1] == '*' || sb[index + 1] == '/' || sb[index + 1] == ')') && num == 1)
                            {
                                sb.Remove(i, index - i + 1);
                                i--;
                            }
                        }
                        break;
                    case '/':
                        index = findNumberEndIndex(sb, i + 1, out num);
                        if (index > i && (index == sb.Length - 1 || sb[index + 1] == '+' || sb[index + 1] == '-' || sb[index + 1] == '*' || sb[index + 1] == '/' || sb[index + 1] == ')') && num == 1)
                        {
                            sb.Remove(i, index - i + 1);
                            i--;
                        }
                        break;
                    case '^':
                        index = findNumberEndIndex(sb, i + 1, out num);
                        if (index > i && num == 1)
                        {
                            sb.Remove(i, index - i + 1);
                            i--;
                        }
                        break;
                }
            }
            return sb.ToString();
        }
        static int findNumberStartIndex(StringBuilder context, int endIndex, out double num)
        {
            StringBuilder sb = new StringBuilder();
            while (endIndex >= 0 && (char.IsDigit(context[endIndex]) || context[endIndex] == '.'))
            {
                sb.Insert(0, context[endIndex]);
                endIndex--;
            }
            num = sb.Length == 0 ? double.NaN : double.Parse(sb.ToString());
            return endIndex + 1;
        }
        static int findNumberEndIndex(StringBuilder context, int startIndex, out double num)
        {
            StringBuilder sb = new StringBuilder();
            while (startIndex < context.Length && (char.IsDigit(context[startIndex]) || context[startIndex] == '.'))
            {
                sb.Append(context[startIndex]);
                startIndex++;
            }
            num = sb.Length == 0 ? double.NaN : double.Parse(sb.ToString());
            return startIndex - 1;
        }
        static int LowestOperatorPriority(string exp)
        {
            int max = -1;
            for (int i = 0; i < exp.Length; i++)
            {
                switch (exp[i])
                {
                    case '+':
                    case '-':
                        if (!IsSign(exp, i))
                        {
                            if (max < 4) max = 4;
                        }
                        break;
                    case '*':
                    case '/':
                    case '%':
                        if (max < 3) max = 3;
                        break;
                    case '^':
                        if (max < 2) max = 2;
                        break;
                }
            }
            return max;
        }
        public static bool IsVertical(double k1, double k2)
        {
            if (k1 == 0 && double.IsInfinity(k2) || double.IsInfinity(k1) && k2 == 0)
            {
                return true;
            }
            return (k1 * k2).AlmostEqual(-1);
        }
        public static PointF[] ToPointFs(IList<PointD> pts)
        {
            PointF[] tmp = new PointF[pts.Count];
            for (int i = 0; i < tmp.Length; i++)
            {
                tmp[i] = pts[i].ToPointF();
            }
            return tmp;
        }
        public static PointD GetUnitVector(double angle)
        {
            return new PointD(Math.Cos(angle), Math.Sin(angle));
        }
        public static double GetSlopeAngle(double slope)
        {
            if (double.IsNaN(slope))
            {
                return 0;
            }
            else
            {
                double res = Math.Atan(slope);
                if (res < 0)
                {
                    res += Math.PI;
                }
                return res;
            }
        }
        public static void ForEachSubString(string exp, string sub, Predicate<int> act)
        {
            if (exp == null || sub == null || act == null) return;
            int lastIndex = 0;
            while (lastIndex < exp.Length)
            {
                lastIndex = exp.IndexOf(sub, lastIndex);
                if (lastIndex >= 0 && act(lastIndex))
                {
                    lastIndex++;
                }
                else
                {
                    break;
                }
            }
        }
        /// <summary>
        /// HSV颜色模型转RGB颜色模型
        /// </summary>
        /// <param name="h">色调(Hue)，取值范围：[0, 360)</param>
        /// <param name="s">饱和度(Saturation)，取值范围：[0, 1]</param>
        /// <param name="v">明度(Value)，取值范围：[0, 1]</param>
        /// <returns></returns>
        public static Color HSVToRGB(float h, float s, float v)
        {
            if (s == 0)
            {
                int tmp = (int)(v * 255);
                return Color.FromArgb(tmp, tmp, tmp);
            }
            float R = 0, G = 0, B = 0;
            int H1 = (int)(h / 60);
            float F = h / 60 - H1, P = v * (1 - s), Q = v * (1 - F * s), T = v * (1 - (1 - F) * s);
            switch (H1)
            {
                case 0: R = v; G = T; B = P; break;
                case 1: R = Q; G = v; B = P; break;
                case 2: R = P; G = v; B = T; break;
                case 3: R = P; G = Q; B = v; break;
                case 4: R = T; G = P; B = v; break;
                case 5: R = v; G = P; B = Q; break;
            }
            R = convert255(R);
            G = convert255(G);
            B = convert255(B);
            return Color.FromArgb((int)R, (int)G, (int)B);
        }
        //public static float[] RGBToHSV(Color cl)
        //{
        //    float H = 0, S, V, R = cl.R / 255f, G = cl.G / 255f, B = cl.B / 255f, min = Math.Min(Math.Min(R, G), B), max = Math.Max(Math.Max(R, G), B);
        //    // H
        //    if (max == min)
        //    {
        //        H = 0;
        //    }
        //    else if (max == R && G > B)
        //    {
        //        H = 60f * (G - B) / (max - min);
        //    }
        //    else if (max == R && G < B)
        //    {
        //        H = 60f * (G - B) / (max - min) + 360;
        //    }
        //    else if (max == G)
        //    {
        //        H = 60f * (B - R) / (max - min) + 120;
        //    }
        //    else if (max == B)
        //    {
        //        H = 60f * (R - G) / (max - min) + 240;
        //    }
        //    // S
        //    if (max == 0)
        //    {
        //        S = 0;
        //    }
        //    else
        //    {
        //        S = (max - min) / max;
        //    }
        //    // V
        //    V = max;
        //    return new float[] { H, S, V };cl.GetHue
        //}
        static float convert255(float value)
        {
            value *= 255;
            while (value > 255) value -= 255;
            while (value < 0) value += 255;
            return value;
        }
        public static void ExploreFile(string filePath)
        {
            try
            {
                Process.Start(@"explorer.exe", "/select,\"" + filePath + "\"");
            }
            catch (Exception)
            {
                IntPtr pidlList = ILCreateFromPathW(filePath);
                if (pidlList != IntPtr.Zero)
                {
                    try
                    {
                        Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0));
                    }
                    finally
                    {
                        ILFree(pidlList);
                    }
                }
            }
        }
        public static Process GetProcess(string processName)
        {
            Process[] processes = Process.GetProcessesByName(processName);
            if (processes == null || processes.Length == 0) return null;
            return processes[0];
        }
    }
}
